import { BaseGraph, IBaseProps, IBoundBox } from './base'

export interface IPolyline extends IBaseProps {
  points: Array<Array<number>>
  smooth?: boolean
  close?: boolean
}

export class Polyline extends BaseGraph {
  protected _points: Array<Array<number>> = []
  protected _smooth = false
  protected _close = false
  protected _tension = 0.5
  constructor(props: IPolyline) {
    super(props)
    this.type = 'polyline'
    this.defaultOption.strokeStyle = this.defaultOption.fillStyle
    this._points = props.points
    this._smooth = !!props.smooth
    this._close = !!props.close
    this.setPath()
  }
  private getCatmullRomControlPoints(p0: Array<number>, p1: Array<number>, p2: Array<number>, p3: Array<number>) {
    const cp1x = p1[0] + ((p2[0] - p0[0]) / 6) * this._tension
    const cp1y = p1[1] + ((p2[1] - p0[1]) / 6) * this._tension

    const cp2x = p2[0] - ((p3[0] - p1[0]) / 6) * this._tension
    const cp2y = p2[1] - ((p3[1] - p1[1]) / 6) * this._tension

    return { cp1x, cp1y, cp2x, cp2y }
  }
  public setPath() {
    delete this.path
    this.path = new Path2D()
    if (this._points.length == 0) return
    this.path.moveTo(this._points[0][0], this._points[0][1])
    if (this._smooth && this._points.length > 2) {
      let first = this._points[0]
      let end = this._points[this._points.length - 1]
      let result = []
      if (this._close) result = [end, ...this._points, first, this._points[1]]
      else result = [first, ...this._points, end]
      for (let i = 1; i < result.length - 2; i++) {
        let p = this.getCatmullRomControlPoints(result[i - 1], result[i], result[i + 1], result[i + 2])
        this.path.bezierCurveTo(p.cp1x, p.cp1y, p.cp2x, p.cp2y, result[i + 1][0], result[i + 1][1])
      }
      if (this._close) this.path.closePath()
    } else {
      // 折线
      for (let i = 1; i < this._points.length; i++) {
        this.path.lineTo(this._points[i][0], this._points[i][1])
      }
      if (this._close) this.path.closePath()
    }
  }
  public drawPath(ctx: CanvasRenderingContext2D): void {
    ctx.stroke(this.path)
  }
  public clone() {
    return new Polyline({
      ...this.option,
      ...this.getBaseCloneAttr(),
      points: JSON.parse(JSON.stringify(this._points)),
      smooth: this._smooth,
      close: this._close
    })
  }
  public getBoundBox(): IBoundBox {
    let minX = Infinity
    let maxX = -Infinity
    let minY = Infinity
    let maxY = -Infinity
    for (let i = 0; i < this._points.length; i++) {
      let p = this._points[i]
      minX = minX < p[0] ? minX : p[0]
      minY = minY < p[1] ? minY : p[1]
      maxX = maxX > p[0] ? maxX : p[0]
      maxY = maxY > p[1] ? maxY : p[1]
    }
    return {
      top: minY + this.y,
      bottom: maxY + this.y,
      left: minX + this.x,
      right: maxX + this.x,
      width: maxX - minX,
      height: maxY - minY
    }
  }
}
